I Deployed a `coturn` Server to AWS
#coding #theWeb #learning #devOps #aws
Thanks to https://gabrieltanner.org/blog/turn-server/, last night I successfully deployed my first coturn
server on an aws ec2 instance. Why you might ask? I am building a thing which needs a WebRTC Peer Connection! Everything in the guide worked as advertised, but I want to have this repeatable. While Terraform is no longer the open source sweetheart it was when I started learning it 6 months ago, I wanted to build on the little knowledge I have of it. Plus I'm using it for personal reasons so it's still fine as far as licenses are concerned.
Scripting the setup
please ignore the broken syntax highlighting and random backticks. There's a bug somewhere and I'm in the process of fixing it but the bug is breaking my whole blog so I'd rather this one post be a bit broken than the whole blog. Thanks!
Maybe there is a better way, but it seems straightforward to add an init template that runs when the instance starts. The template is a bash script that installs and configures the coturn server, as well as certbot as the article above suggests. One thing that will need to be done in the terraform is opening the proper ports. The default security group won't have the proper config so make sure not to skip that part, otherwise you'll see timeout errors on the testing site.
You can choose to skip user credentials on the turnserver depending on your use case. I'm going to set them up via the suggested CLI args in the "docs", which as far as I can tell is limited to this very well commented .conf
file.
This is meant for running as an init script on aws ec2. I am using ubuntu since I don't want to deal withyum
or building the coturn
source.
Feel free to use the code below. No guarantees or warranties đ.
The Script
TLDR: install coturn and certbot, configure them, and flip the ON switch!
1
2#!/bin/sh
3
4# shellcheck disable=SC2154
5
6
7
8echo 'Initializing turn server...'
9
10
11
12set -e
13
14
15
16#ubuntu needs root
17
18sudo -i
19
20
21
22echo 'Installing stuff...'
23
24#install coturn and cerbot
25
26apt-get update -y
27
28apt-get install coturn certbot -y
29
30
31
32echo 'Setting config...'
33
34# enable the server
35
36cat >> /etc/default/coturn << EOF
37
38TURNSERVER_ENABLED=1
39
40EOF
41
42
43
44# start the service
45
46systemctl start coturn
47
48
49
50#get the ip of the current ec2 instace
51
52PUBLIC_IP=$(curl http://169.254.169.254/latest/meta-data/public-ipv4)
53
54INTERNAL_IP=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)
55
56DOMAIN=${domain}
57
58FQDN=${subdomain}.${domain}
59
60EMAIL=${email}
61
62COTURN_USER=${username}
63
64COTURN_PASS=${password}
65
66# Don't forget any DNS related credentials
67
68
69USER_INFO=$COTURN_USER:$COTURN_PASS
70
71# or for an encrypted pw:
72# USER_INFO=$(turnadmin -k -u "$COTURN_USER" -r "$DOMAIN" -p "$COTURN_PASS" | head -n 1)
73
74
75
76# set the config
77
78cat >> /etc/turnserver.conf << EOF
79
80realm=$DOMAIN
81
82server-name=$FQDN
83
84external-ip=$PUBLIC_IP/$INTERNAL_IP
85
86cert=/etc/coturn/certs/$FQDN.cert
87
88pkey=/etc/coturn/certs/$FQDN.key
89
90user=$USER_INFO
91
92lt-cred-mech
93
94fingerprint
95
96EOF
97
98
99
100echo 'Creating DNS record...'
101
102# update the dns records at your DNS provider with the public ip
103
104
105# wait so that we can ensure the dns record is created and propogated
106# might not be necessary but seemed safer than not waiting
107
108sleep 30
109
110
111
112echo 'Creating cert...'
113
114# set the cert
115
116certbot certonly -n --agree-tos --standalone --preferred-challenges http \
117
118--deploy-hook "systemctl restart coturn" \
119
120-d "$FQDN" \
121
122-m "$EMAIL"
123
124
125
126# was facing an issue like this without copying the certs to a dir
127# that coturn could read:
128# https://github.com/coturn/coturn/issues/1139
129
130mkdir -p /etc/coturn/certs
131
132cp "/etc/letsencrypt/live/$FQDN/cert.pem" "/etc/coturn/certs/$FQDN.cert"
133
134cp "/etc/letsencrypt/live/$FQDN/privkey.pem" "/etc/coturn/certs/$FQDN.key"
135
136chown turnserver -R "/etc/coturn/certs"
137
138chmod 700 -R "/etc/coturn/certs"
139
140
141
142service coturn restart
143
144
145
146echo 'Done!'
147
The Terraform
And the terraform: TLDR: Spins up a keypair to be able to ssh into our ec2 instance, and the proper iam roles and permissions to do things in the least permissive way. A security group is also created with the proper ports open for our coturn server to talk to the rest of the web.
1
2terraform {
3 required_providers {
4 aws = {
5 source = "hashicorp/aws"
6 version = "~> 5.0"
7 }
8 }
9
10 required_version = ">= 1.2.0"
11}
12
13provider "aws" {
14 region = var.aws_region
15}
16
17locals {
18 user_data = templatefile("${path.module}/init.tftpl", {
19 subdomain = var.subdomain
20 domain = var.domain
21 username = var.coturn_user
22 password = var.coturn_pass
23 email = var.email
24 # any extra vars needed for DNS
25 })
26}
27
28# key pair used to ssh in to the ec2 instance
29resource "aws_key_pair" "deployer" {
30 key_name = "coturn_keypair"
31 public_key = file(var.public_key)
32}
33
34resource "aws_instance" "app_server" {
35 instance_type = "t3.nano"
36 # ubuntu 22.10
37 ami = "ami-0fc5d935ebf8bc3bc"
38 key_name = aws_key_pair.deployer.key_name
39 iam_instance_profile = aws_iam_instance_profile.coturn.name
40 security_groups = [aws_security_group.coturn_sg.name]
41 root_block_device {
42 delete_on_termination = true
43 }
44 # init script
45 user_data = local.user_data
46 tags = {
47 Name = "Coturn"
48 }
49}
50
51# IAM roles and policy docs
52
53# IAM role for the instance profile
54resource "aws_iam_role" "role" {
55 name = "coturn_iam_role_ec2"
56 path = "/"
57 assume_role_policy = data.aws_iam_policy_document.assume_role.json
58}
59
60# attach role to an instance profile for use with the ec2 instance
61resource "aws_iam_instance_profile" "coturn" {
62 name = "coturn_iam_profile_ec2"
63 role = aws_iam_role.role.name
64 tags = {
65 Name = "CoturnAdmin"
66 }
67}
68
69# ec2 role
70data "aws_iam_policy_document" "assume_role" {
71 statement {
72 sid = 1
73 effect = "Allow"
74 principals {
75 type = "Service"
76 identifiers = ["ec2.amazonaws.com"]
77 }
78
79 actions = ["sts:AssumeRole"]
80 }
81}
82
83# security group to open ports
84resource "aws_security_group" "coturn_sg" {
85 name = "coturn_sg"
86 description = "security group for the coturn ec2 instance"
87
88 ingress {
89 from_port = 3478
90 to_port = 3479
91 protocol = "tcp"
92 cidr_blocks = ["0.0.0.0/0"]
93 }
94
95 ingress {
96 from_port = 3478
97 to_port = 3479
98 protocol = "udp"
99 cidr_blocks = ["0.0.0.0/0"]
100 }
101
102 # tls
103 ingress {
104 from_port = 5349
105 to_port = 5350
106 protocol = "tcp"
107 cidr_blocks = ["0.0.0.0/0"]
108 }
109
110 ingress {
111 from_port = 80
112 to_port = 80
113 protocol = "tcp"
114 cidr_blocks = ["0.0.0.0/0"]
115 }
116
117 egress {
118 from_port = 0
119 to_port = 0
120 protocol = "-1"
121 cidr_blocks = ["0.0.0.0/0"]
122 }
123}
124
And the tf vars file:
1
2variable "aws_region" {
3 description = "AWS region"
4 type = string
5 default = "us-east-1"
6}
7
8variable "public_key" {
9 description = "path to AWS keypair public key"
10 type = string
11 default = "~/.ssh/aws_keypair.pub"
12}
13
14variable "domain" {
15 description = "the domain name of the server"
16 type = string
17}
18
19variable "subdomain" {
20 description = "the subdomain of the server"
21 type = string
22}
23
24variable "username" {
25 description = "the username for the lts creds"
26 type = string
27}
28
29variable "password" {
30 description = "the pass for the lts creds"
31 type = string
32}
33variable "email" {
34 description = "the email address for the ssl cert"
35 type = string
36}
37
With these combined, you should be able to deploy a coturn
server with the flick of the terraform apply wrist!
If you have a new A record on your DNS provider, that means at least that step worked.
If you need to SSH into the instance, make sure to open up SSH port 22 in the security group for your IP. You can do this in the terraform if you want, but I wouldn't expect to need to get in to the server much except to grab the encrypted password. Also the AWS console offers a nice "My IP" option. I know it's possible to code this into the terraform but it seemed like too much effort for not a lot gained.
Test the server with https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/
Resources
These resources were extremely helpful in learning and implementing all of this WebRTC stuff:
https://gabrieltanner.org/blog/turn-server https://web.dev/articles/webrtc-basics https://web.dev/articles/webrtc-infrastructure https://codelabs.developers.google.com/codelabs/webrtc-web/#0 https://www.baeldung.com/webrtc https://webrtc.github.io/samples/ https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/
And for actually implementing WebRTC in an application, specifically SvelteKit, these were helpful. I ended up using Socket.io instead of ws
as implemented in the example:
https://github.com/suhaildawood/SvelteKit-integrated-WebSocket
This was helpful too as I had never worked with websockets before: https://joyofcode.xyz/using-websockets-with-sveltekit
Last updated: